﻿using System;
using System.Reflection;
using System.Threading;
using System.Runtime.Remoting;

namespace Csharp.AppDomains {

    /// <summary>
    /// This is an interpretation of Jeffrey Richter's example
    /// CLR through C# (p. 527-530)
    /// </summary>
    public class CrossingBoundaries {

        public static void Main() {
            AppDomain adCallingThreadDomain = Thread.GetDomain();
            string callingDomainName = adCallingThreadDomain.FriendlyName;
            Console.WriteLine("Default AppDomain's friendly name={0}", callingDomainName);

            string exeAssembly = Assembly.GetEntryAssembly().FullName;
            Console.WriteLine("Main assembly={0}", exeAssembly);

            AppDomain ad2 = null;

            // DEMO 1: Cross-AppDomain communication using marshal-by-reference
            Console.WriteLine("{0}Demo #1: Marshal-by-Reference", Environment.NewLine);

            ad2 = AppDomain.CreateDomain("AD #2", null, null);

            MarshalByRefType mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "Csharp.AppDomains.MarshalByRefType");
            Type t = mbrt.GetType();

            Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
            
            mbrt.SomeMethod(callingDomainName);
            AppDomain.Unload(ad2);

            try {
                mbrt.SomeMethod(callingDomainName);
                Console.WriteLine("Successful call.");
            } catch (AppDomainUnloadedException) {
                Console.WriteLine("Failed call.");
            }

            // DEMO 2: Cross-AppDomain communication using Marshal-by-Value
            Console.WriteLine("{0}Demo #2: Marshal-by-Value", Environment.NewLine);

            ad2 = AppDomain.CreateDomain("AD #2", null, null);
            MarshalByValType mbvt = (MarshalByValType)ad2.CreateInstanceAndUnwrap(exeAssembly, "Csharp.AppDomains.MarshalByValType");

            Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbvt));
            mbvt.SomeMethod(callingDomainName);

            AppDomain.Unload(ad2);

            try {
                mbvt.SomeMethod(callingDomainName);
                Console.WriteLine("Successful call.");
            } catch (AppDomainUnloadedException) {
                Console.WriteLine("Failed call.");
            }


            // DEMO 3: Cross-AppDomain communication using non-marshalable type
            Console.WriteLine("{0}Demo #3: Non-Marshalable Type", Environment.NewLine);

            ad2 = AppDomain.CreateDomain("AD #2", null, null);

            NonMarshalableType nmt = (NonMarshalableType)ad2.CreateInstanceAndUnwrap(exeAssembly, "Csharp.AppDomains.NonMarshalableType");

            
            Console.ReadKey(true);
        }
    }

    public class MarshalByRefType : MarshalByRefObject {
        DateTime creation = DateTime.Now;

        public MarshalByRefType() {
            Console.WriteLine("{0} ctor running in {1}",
                this.GetType().ToString(),
                Thread.GetDomain().FriendlyName);
        }

        public void SomeMethod(string callingDomainName) {
            Console.WriteLine("Calling from '{0}' to '{1}'.",
                callingDomainName,
                Thread.GetDomain().FriendlyName);
        }
    }

    [Serializable]
    public class MarshalByValType : Object {
        DateTime creation = DateTime.Now;

        public MarshalByValType() {
            Console.WriteLine("{0} ctor running in {1}",
                this.GetType().ToString(),
                Thread.GetDomain().FriendlyName);
        }

        public void SomeMethod(string callingDomainName) {
            Console.WriteLine("Calling from '{0}' to '{1}'.",
                callingDomainName,
                Thread.GetDomain().FriendlyName);
        }
    }

    public class NonMarshalableType : Object {
        DateTime creation = DateTime.Now;

        public NonMarshalableType() {
            Console.WriteLine("{0} ctor running in {1}",
                this.GetType().ToString(),
                Thread.GetDomain().FriendlyName);
        }

        public void SomeMethod(string callingDomainName) {
            Console.WriteLine("Calling from '{0}' to '{1}'.",
                callingDomainName,
                Thread.GetDomain().FriendlyName);
        }
    }
}
